<?php

/**
 * Copyright (c) 2011 Christopher John Jackson
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
class oEmbedder {

    private $url;
    private $urlencoded;
    private $providers;
    private $oembedmeta;
    private $discovery;
    private $maxWidth;
    private $maxHeight;

    public function __construct() {
        $this->url = '';
        $this->urlencoded = '';
        $this->oembedmeta = false;
        $this->discovery = false;
        $this->maxWidth = false;
        $this->maxHeight = false;

        $this->providers = array(
            '#http://(.{0,}).youtube.com/watch(.{1,})#i' => 'http://www.youtube.com/oembed?url=__URL__&format=json',
            '#http://youtu.be/(.{1,})#i' => 'http://www.youtube.com/oembed?url=__URL__&format=json',
            '#http://(.{0,}).flickr.com/(.{1,})#i' => 'http://flickr.com/services/oembed?url=__URL__',
            '#http://(.{0,}).viddler.com/(.{1,})#i' => 'http://lab.viddler.com/services/oembed/?url=__URL__',
            '#http://qik.com/video/(.{1,})#i' => 'http://qik.com/api/oembed.xml?url=__URL__',
            '#http://(.{0,}).revision3.com/(.{1,})#i' => 'http://revision3.com/api/oembed/?url=__URL__',
            '#http://www.hulu.com/watch/(.{1,})#i' => 'http://www.hulu.com/api/oembed.xml?url=__URL__',
            '#http://(.{0,})vimeo.com/groups/(.{1,})/(.{1,})#i' => 'http://www.vimeo.com/api/oembed.xml?url=__URL__',
            '#http://(.{0,})vimeo.com/(.{1,})#i' => 'http://www.vimeo.com/api/oembed.xml?url=__URL__',
            '#http://www.ifixit.com/Guide/View/(.{1,})#i' => 'http://www.ifixit.com/Embed?url=__URL__&format=JSON',
            '#http://(.{1,}).smugmug.com/(.{1,})#i' => 'http://api.smugmug.com/services/oembed/?url=__URL__',
            '#http://(.{1,}).example.com/(.{1,})#i' => 'http://api.smugmug.com/services/oembed/?url=__URL__'
        );
    }

    public function parseUrl($url) {
        $this->url = $url;
        $this->urlencoded = urlencode($url);

        $providerUrl = $this->getProviderUrl($url);
        if ($providerUrl) {
            $this->setUpMeta($providerUrl);
        } else {
            $this->oembedmeta = false;
        }
    }

    public function setMeta($meta) {
        if (is_array($meta)) {
            $this->oembedmeta = $meta;
        }
    }

    public function setEmbedlyKey($key) {
        include 'embedly.stubs.php';
        foreach ($embedly as $pattern => $endpoint) {
            $endpoint = str_replace('__KEY__', $key, $endpoint);
            $this->providers[$pattern] = $endpoint;
        }
    }

    public function getMeta() {
        return $this->oembedmeta;
    }

    public function getMetaJSON() {
        return json_encode($this->oembedmeta);
    }

    public function getUrl() {
        return $this->url;
    }

    public function setUrl($url) {
        $this->url = $url;
    }

    public function setMaxWidth($maxWidth) {
        if (is_numeric($maxWidth)) {
            $this->maxWidth = $maxWidth;
        }
    }

    public function setMaxHeight($maxHeight) {
        if (is_numeric($maxHeight)) {
            $this->maxHeight = $maxHeight;
        }
    }

    public function enableDiscovery() {
        $this->discovery = true;
    }

    private function getProviderUrl($url) {
        $providers = $this->providers;
        foreach ($providers as $pattern => $value) {
            if (preg_match($pattern, $url)) {
                if ($this->maxWidth) {
                    $value .= '&maxwidth=' . $this->maxWidth;
                }
                if ($this->maxHeight) {
                    $value .= '&maxheight=' . $this->maxHeight;
                }
                return str_replace('__URL__', $this->urlencoded, $value);
            }
        }
        if ($this->discovery) {
            return $this->attempDiscovery($url);
        }
        return false;
    }

    private function setUpMeta($providerUrl) {
        $file = @file_get_contents($providerUrl);
        $json = json_decode($file, true);
        if ($json) {
            $this->oembedmeta = $json;
        } else {
            //Try XML.
            $this->setUpMetaXml($file);
        }
    }

    private function setUpMetaXml($file) {
        $xml = simplexml_load_string($file);
        if (!$xml) {
            $this->oembedmeta = false;
            return;
        }

        $array = array();
        foreach ($xml->children() as $child) {
            $strlen = strlen($child->getName());
            $value = substr($child->asXML(), $strlen + 2);
            $value = substr($value, 0, -$strlen - 3);
            $array[$child->getName()] = $value;
        }

        if (!count($array)) {
            $this->oembedmeta = false;
            return;
        }

        $this->oembedmeta = $array;
    }

    private function attempDiscovery($url) {
        $file = @file_get_contents($url);
        if (preg_match('#<link ([^>]{1,})application/json\+oembed([^>]{1,})(/{0,1})>#is', $file, $match)) {
            return $this->linkFromDiscovery($match[0]);
        }
        if (preg_match('#<link ([^>]{1,})text/xml\+oembed([^>]{1,})(/{0,1})>#is', $file, $match)) {
            return $this->linkFromDiscovery($match[0]);
        }
        return false;
    }

    private function linkFromDiscovery($match) {
        if (preg_match('#href="([^"]{1,})"#i', $match, $matches)) {
            return $matches[1];
        }
        return false;
    }

    public function isValid() {
        if ($this->oembedmeta) {
            return true;
        }
        return false;
    }

    public function fetchCode() {
        $meta = $this->oembedmeta;
        if (isset($meta['type'])) {
            if (method_exists($this, $meta['type'] . '_code')) {
                return call_user_func(array($this, $meta['type'] . '_code'), $meta);
            }
        }
        return $this->url;
    }

    private function photo_code($meta) {
        $title = '';
        if (isset($meta['title'])) {
            $title = $meta['title'];
        }
        if (!(isset($meta['url']) || isset($meta['width']) || isset($meta['height']))) {
            return $this->url;
        }
        $url = $meta['url'];
        $width = $meta['width'];
        $height = $meta['height'];
        ?>
        <img src="<?php echo $url; ?>" title="<?php echo $title ?>"
             width="<?php echo $width ?>" height="<?php echo $height ?>" />
        <?php
    }

    private function video_code($meta) {
        if (isset($meta['html'])) {
            return $meta['html'];
        }
        return $this->url;
    }

    private function link_code($meta) {
        //Not Supported. Too complicated.
        return $this->url;
    }

    private function rich_code($meta) {
        if (isset($meta['html'])) {
            return $meta['html'];
        }
        return $this->url;
    }

}